home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************************************
- *
- *
- * ObjectMacZapp -- a standard Mac OOP application template
- *
- *
- *
- * ZJPEGFile.cpp -- a file object that can open JPEG images
- *
- *
- *
- *
- *
- * © 1996, Graham Cox
- *
- *
- *
- *
- *************************************************************************************************/
-
- #include "ZJPEGFile.h"
- #include "ZGWorld.h"
- #include "MacZoop.h"
-
- #include <ImageCompression.h>
-
- static OSErr ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld );
-
-
- /*------------------------------------- CONSTRUCTOR ------------------------------------------*/
-
-
- ZJPEGFile::ZJPEGFile( const FSSpec& aSpec )
- : ZFile( aSpec )
- {
- FailOSErr( gMacInfo.hasImgCompressionMgr? noErr : kNoSystemSupportForFeatureErr );
- }
-
-
- /***********************************************************************************************
- *
- * READ- read the JPEG file into ZGWorld object (recommended approach)
- *
- ***********************************************************************************************/
-
- void ZJPEGFile::Read( ZGWorld* aGWorld )
- {
- FailNILParam( aGWorld );
-
- if ( refNum == _NOT_OPEN )
- FailOSErr( fnOpnErr );
-
- GWorldPtr gw = aGWorld->GetMacGWorld();
-
- if ( gw )
- DisposeGWorld( gw );
-
- aGWorld->SetMacGWorld( gw = NULL );
-
- Read( &gw );
-
- // now we need to make the ZGWorld object handle this GWorld that was built
- // by the image compression manager. WARNING! If an exception was thrown reading the file
- // the GWorld, the original GWorld will have been discarded and trying to access it via
- // the object will probably crash. However, normally this is called to create a new image
- // from a file, so this shouldn't be a problem. Bear it in mind for other uses however.
-
- aGWorld->SetMacGWorld( gw );
- }
-
-
- /***********************************************************************************************
- *
- * READ- read the JPEG file into a Macintosh GWorld
- *
- ***********************************************************************************************/
-
- void ZJPEGFile::Read( GWorldPtr* aGWorld )
- {
- FailNILParam( aGWorld );
- FailOSErr( ReadJFIFToGWorld( refNum, aGWorld ));
- }
-
-
- #pragma mark -
- #pragma mark #### static JPEG Decoding stuff ####
-
- // prototypes:
-
- static pascal OSErr JPEGDataLoader( Ptr *dataP, long bytesNeeded, long refCon );
- static ImageDescriptionHandle ScanJPEG( short originalFile, Ptr *data, OpenCPicParams *pictureHeader );
- static void SwallowQuantTable( char *data );
- static void SwallowHuffTable( char *data );
- static char *MarkerDetect( char *data, short *width, short *height, long *hRes, long *vRes, short *depth );
-
- // switches, etc:
-
- //#define USE_DATA_LOADER
- #define kDefaultBufferSize (64L * 1024L) // 64K buffer size
-
- /*
- Written by: Mark Krueger, Apple Computer, Inc.
-
- The JPEG File Interchange Format ( JFIF) is a cross platform standard file format for
- storing JPEG compressed image files. This application shows you how you can easily convert
- these to QuickTime PICT format, or vice-verse.
-
- You can use this in your application to open JFIF files directly or put a user interface
- ( Drag and Drop would be cool ) on it to make a conversion program ). When files are to
- be used on the Mac it is best to keep them in QTPict format so they can be easily copied
- and pasted, but JFIF format is useful for transfering data to other platforms that cannot
- use PICT formated JPEG images.
-
- NOTE: the PICT to JFIF format translator is incomplete in that it only converts PICT
- files which are already in QuickTIme JPEG format and it does not handle banded JPEG
- Picts ( which may be commonly created by QuickTime applications which call
- CompressPictureFile in low memory conditions or which create their own banded picts)
- To fully handle these images, the individual bands would have to be converted into a
- single JPEG stream when put into JFIF format, and this code does not show you how
- to do that.
-
- */
-
- // static functions, mostly courtesy Apple Computer DTS:
-
-
-
- static OSErr ReadJFIFToGWorld( short refNum, GWorldPtr* theGWorld )
- {
- // simpler and more robust function for reading JFIF images. Because this does not go through the step of
- // decompressing the image to a picture, it requires less memory than the above function. It resizes the
- // GWorld to the size of the image. This may alter the GWorld ptr value, so be sure to copy it back to any
- // data structures that have reference to it- e.g. layers.
-
- OSErr result = noErr;
- OpenCPicParams header;
- Ptr data = NULL;
- Handle tempBuffer = NULL;
- ImageDescriptionHandle desc = NULL;
- GWorldFlags flags;
- PixMapHandle ppix;
- CGrafPtr savePort;
- GDHandle saveDevice;
- ICMProgressProcRecord iProgRec = {NULL,0};
- ICMDataProcRecord dLoadProc = {NULL,0};
-
- #ifdef USE_DATA_LOADER
- if ((desc = ScanJPEG(refNum,NULL,&header)) == NULL)
- {
- result = paramErr;
- goto done;
- }
- (*desc)->dataSize = 0;
- #else
- if ((desc = ScanJPEG(refNum,&data,&header)) == NULL)
- {
- result = paramErr;
- goto done;
- }
- #endif
- // we have a description of the image. Now resize the GWorld and set its depth. Note that we do not
- // call UpdateGWorld, but simply get rid of the existing one and reallocate it. This requires half the
- // memory it otherwise would because no copy is required. This is ok because we are not attempting to keep
- // the existing image anyway.
-
- if (*theGWorld)
- DisposeGWorld(*theGWorld);
-
- result = NewImageGWorld(theGWorld,desc,0);
-
- if (result)
- goto done;
-
- // we succeeded in changing the GWorld to the size we desire, now we simply decompress the data into
- // the GWorld's pixmap. We load the JPEG data using a small buffer to save memory, so set this up here
-
- #ifdef USE_DATA_LOADER
- dLoadProc.dataProc = NewICMDataProc((ProcPtr) JPEGDataLoader);
- dLoadProc.dataRefCon = refNum;
- result = SetFPos(refNum,fsFromStart,0);
-
- tempBuffer = NewHandleClear(kDefaultBufferSize);
- if (tempBuffer == NULL)
- {
- result = memFullErr;
- goto done;
- }
- else
- {
- MoveHHi(tempBuffer);
- HLock(tempBuffer);
-
- data = StripAddress(*tempBuffer);
- }
- #endif
-
- //iProgRec.progressProc = NewICMProgressProc((ProcPtr) StdDecompressionProgressProc);
- //iProgRec.progressRefCon = 'open';
-
- if (LockPixels(ppix = GetGWorldPixMap(*theGWorld)))
- {
- GetGWorld(&savePort,&saveDevice);
- SetGWorld(*theGWorld,NULL);
-
- #ifdef USE_DATA_LOADER
- result = FDecompressImage( data,desc,ppix,
- NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
- NULL,(Rect *) NULL,codecHighQuality,anyCodec,kDefaultBufferSize,
- (ICMDataProcRecordPtr) &dLoadProc,
- (ICMProgressProcRecordPtr) &iProgRec);
- #else
- result = FDecompressImage( data,desc,ppix,
- NULL,NULL,srcCopy + ditherCopy,(RgnHandle) NULL,
- NULL,(Rect *) NULL,codecHighQuality,anyCodec,0,
- (ICMDataProcRecordPtr) NULL,
- (ICMProgressProcRecordPtr) &iProgRec);
- #endif
- SetGWorld(savePort,saveDevice);
- UnlockPixels(ppix);
- }
- else
- result = -50;
- done:
- if (tempBuffer)
- {
- HUnlock(tempBuffer);
- DisposeHandle(tempBuffer);
- }
- if (desc)
- DisposeHandle((Handle) desc);
- if (iProgRec.progressProc)
- DisposeRoutineDescriptor(iProgRec.progressProc);
- if (dLoadProc.dataProc)
- DisposeRoutineDescriptor(dLoadProc.dataProc);
- return result;
- }
-
-
-
- static pascal OSErr JPEGDataLoader(Ptr *dataP,long bytesNeeded,long refCon)
- {
- OSErr theErr = noErr;
- short fileRefNum = LoWord(refCon);
-
- if (dataP)
- theErr = FSRead(fileRefNum,&bytesNeeded,*dataP);
- else
- theErr = SetFPos(fileRefNum,fsFromMark,bytesNeeded);
- return theErr;
- }
-
-
- /************************************************
-
- Scan a file for valid JPEG data, and fill in a picture header and ImageDescription
- for it.
-
- *************************************************/
-
- static ImageDescriptionHandle ScanJPEG(short originalFile,Ptr *data,OpenCPicParams *pictureHeader)
- {
- short w,h;
- ImageDescriptionHandle desc;
- long l;
- char *bitStream,*scanData,*buffer;
- long hRes = 72L<<16,vRes = 72L<<16;
- short depth = 32;
-
- // this creates a buffer which holds the whole image. This is not very efficient. Instead we will spool
- // the data in using a data loading procedure.
-
-
- GetEOF(originalFile,&l);
- if ((buffer = NewPtr(l)) == nil)
- return(nil);
-
- FSRead(originalFile,&l,buffer);
- bitStream = buffer;
-
- if ( (desc = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription))) == nil )
- return(nil);
-
- if ( (scanData = MarkerDetect(bitStream,&w,&h,&hRes,&vRes,&depth)) == 0 )
- return(nil);
-
- (*desc)->idSize = sizeof(ImageDescription);
- (*desc)->width = w;
- (*desc)->height = h;
- (*desc)->temporalQuality = 0;
- (*desc)->spatialQuality = codecNormalQuality;
- (*desc)->dataSize = l;
- (*desc)->cType = 'jpeg';
- (*desc)->version = 0;
- (*desc)->revisionLevel = 0;
- (*desc)->vendor = 0;
- (*desc)->hRes = hRes;
- (*desc)->vRes = vRes;
- (*desc)->depth = depth;
- (*desc)->clutID = -1;
- BlockMoveData("\pPhoto",(*desc)->name,6);
- SetRect(&pictureHeader->srcRect,0,0,w,h);
- pictureHeader->version = -2;
- pictureHeader->reserved1 = 0;
- pictureHeader->reserved2 = 0;
- pictureHeader->hRes = hRes;
- pictureHeader->vRes = vRes;
-
- if (data == NULL)
- {
- // get rid of this buffer- we are going to load the image in small pieces
-
- DisposePtr(buffer);
- }
- else
- *data = buffer;
-
- return(desc);
- }
-
-
-
- /**********************************************************************
-
- JPEG specific stuff.
-
- ***********************************************************************/
-
- /*
-
- JPEG Marker code definitions.
-
- */
-
- #define MARKER_PREFIX 0xff
- #define MARKER_SOI 0xd8 /* start of image */
- #define MARKER_SOF 0xc0 /* start of frame */
- #define MARKER_DHT 0xc4 /* define Huffman table */
- #define MARKER_EOI 0xd9 /* end of image */
- #define MARKER_SOS 0xda /* start of scan */
- #define MARKER_DQT 0xdb /* define quantization tables */
- #define MARKER_DNL 0xdc /* define quantization tables */
- #define MARKER_DRI 0xdd /* define Huffman table */
- #define MARKER_COM 0xfe /* comment */
- #define MARKER_APP0 0xe0
-
-
- /**********************************************************************
-
- Read the quantization table from the JPEG bitstream.
-
- ***********************************************************************/
-
- static void SwallowQuantTable(char *data)
- {
- long i;
- long length,pm,nm;
-
- length = *(short *)data; /* read length */
- length -= 2;
- data += 2;
- while ( length ) {
- nm= *data++; /* read precision and number */
- pm = nm>>4;
- nm &= 0xf;
- length--;
- if ( pm ) {
- for(i=0;i<64;i++) {
- length -= 2;
- data += 2;
- }
- } else {
- for(i=0;i<64;i++) {
- length--;
- data++;
- }
- }
- }
- }
-
- /**********************************************************************
-
- Read the huffman table from the JPEG bitstream.
-
- ***********************************************************************/
-
- static void SwallowHuffTable(char *data)
- {
- short i,tc,id;
- long length;
-
- unsigned char bin[17];
- unsigned char val[256];
-
- bin[0] = 0;
- length = *(short *)data; /* read length */
- data += 2;
- length -= 2;
- while ( length ) {
- id=*data++; /* read id */
- length--;
- if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
- return;
- }
- tc = 0;
- for(i=0;i<16;i++) {
- length--;
- tc += (bin[i+1] = *data++);
- }
- for (i=0; i < tc; i++ ) {
- length--;
- val[i] = *data++;
- }
- }
- }
-
-
-
- /**********************************************************************
-
- Scan the JPEG stream for the proper markers and fill in the image parameters
-
- returns nil if it cant comprehend the data, otherwise a pointer to the start
- of the JPEG data.
-
-
- It does a cursory check on the JPEG data to see if it's reasonable.
- Check out the ISO JPEG spec if you really want to know what's going on here.
-
- ***********************************************************************/
-
- static char *MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
- {
- short frame_field_length;
- short data_precision;
- short scan_field_length;
- short number_component,scan_components;
- short c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
- short dac_t1, dac_t2, dac_t3;
- unsigned char c;
- short qtabledefn;
- short htabledefn;
- short status;
- short length;
- short i;
-
- c = *data++;
- qtabledefn = 0;
- htabledefn = 0;
- status = 0;
- while (c != (unsigned char)MARKER_SOS) {
- while (c != (unsigned char)MARKER_PREFIX)
- c = *data++; /* looking for marker prefix bytes */
- while (c == (unsigned char)MARKER_PREFIX)
- c = *data++; /* (multiple?) marker prefix bytes */
- if (c == 0)
- continue; /* 0 is never a marker code */
-
- if (c == (unsigned char)MARKER_SOF) {
-
- frame_field_length = *(short *)data;
- data += 2;
- data_precision = *data++;
-
- if ( data_precision != 8 ) {
- status = 2;
- }
-
- *height = *(short *)data;
- data += 2;
- *width = *(short *)data;
- data += 2;
-
- number_component = *data++;
-
- switch ( number_component ) {
- case 3:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- c2 = *data++;
- hv2 = *data++;
- q2 = *data++;
- c3 = *data++;
- hv3 = *data++;
- q3 = *data++;
- *depth = 32;
- break;
- case 1:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- *depth = 40;
- break;
- default:
- status = 3;
- break;
- }
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOS) {
- short tn;
- scan_field_length = *(short *)data;
- data += 2;
- scan_components = *data++;
- for ( i=0; i < scan_components; i++ ) {
- unsigned char cn,dac_t;
-
- cn = *data++;
- dac_t = *data++;
- if ( cn == c1 ) {
- dac_t1 = dac_t;
- } else if ( cn == c2 ) {
- dac_t2 = dac_t;
- } else if ( cn == c3 ) {
- dac_t3 = dac_t;
- } else {
- status = 29;
- break;
- }
- }
- switch ( tn=(dac_t1 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t2 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t3 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
-
-
- /* Initialize the DC tables */
-
- switch ( tn=dac_t1 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t2 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t3 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- if ( *data++ != 0 ) {
- // status = 18;
- }
- if ( *data++ != 63 ) {
- // status = 19;
- }
- if ( *data++ != 0 ) {
- // status = 20;
- }
- if ( status )
- return(0);
- else
- return(data);
- }
-
- if (c == (unsigned char)MARKER_DQT) {
- scan_field_length = *(short *)data;
- SwallowQuantTable(data);
- data += scan_field_length;
- continue;
- }
- if (c == (unsigned char)MARKER_DHT) {
- scan_field_length = *(short *)data;
- SwallowHuffTable(data);
- continue;
- }
- if (c == (unsigned char)MARKER_DRI) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c == (unsigned char)MARKER_DNL) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI) /* image start, end marker */
- continue;
-
- if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
- status = 12;
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
- length = *(short *)data; /* read length */
- data += 2;
- length -= 2;
- if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
- char buf[5];
- buf[0] = *data++;
- buf[1] = *data++;
- buf[2] = *data++;
- buf[3] = *data++;
- buf[4] = *data++;
- length -= 5;
-
- if ( buf[0] == 'J' && buf[1] == 'F' && buf[2] == 'I' && buf[3] == 'F' ) {
- short units;
- long xres,yres;
- short version;
-
-
- version = *(short *)data; data += 2;length -= 2;
- if ( version != 0x100 && version != 0x101 && version != 0x102) {
- status = 44; // unknown JFIF version
- break;
- }
- units = *data++; length--;
- xres = *(short *)data; data += 2; length -= 2;
- yres = *(short *)data; data += 2; length -= 2;
-
- switch ( units ) {
- case 0: // no res, just aspect ratio
- // some files will have bad res data here. In this case, xRes & yRes will
- // be zero. This really means that they should be 1! This allows some dodgy
- // JPEGs created by some crappy PC software to be opened
-
- if (xres == 0)
- xres = 1;
- if (yres == 0)
- yres = 1;
-
- *hRes = FixMul(72L<<16,xres<<16);
- *vRes = FixMul(72L<<16,yres<<16);
- break;
- case 1: // dots per inch
- *hRes = xres<<16;
- *vRes = yres<<16;
- break;
- case 2: // dots per centimeter (we convert to dpi )
- *hRes = FixMul(0x28a3d,xres<<16);
- *vRes = FixMul(0x28a3d,xres<<16);
- break;
- default:
- break;
- }
- xres = *data++; length--;
- yres = *data++; length--;
-
- /* skip JFIF thumbnail */
-
- xres *= yres;
- data += xres*3; length -= xres*3;
-
- if ( length != 0 ) {
- status = 44; // bad jfif marker
- break;
- }
- }
- }
- data += length;
- continue;
- }
- if (c == (unsigned char)MARKER_COM) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if ( c == 0x1 )
- continue;
- if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
- length = *(short *)data; /* read length */
- status = 13;
- data += length;
- continue;
- }
- }
- return(0);
- }
-
-